package com.example;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author twj
 * @description Optional案例
 * @time 2023/9/3^14:03:39
 */
public class OptionalDemo {
    //JDK8引入的Optional就是用来以更优雅的代码避免空指针异常！！！
    public static void main(String[] args) {
        Author author = getAuthor();
        //传统处理方式
        if (author != null) {
            System.out.println(author.getName());
        }
        /* Tip-1
         * Optional就好像是包装类，可以把我们的具体数据封装到Optional对象内部
         * 然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常
         * ，我们一般使用Optional的静态方法 ofNullable() 来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。
         */
        Optional<Author> author1 = Optional.ofNullable(author);
        // Tip-5 通过 ifPresent() 方法我们可以安全消费值，如果有数据就进行后续处理
        author1.ifPresent(a1 -> System.out.println(a1.getName()));

        /* Tip-2
         * 如果还要加一行代码来封装数据会比较麻烦，但是如果改造下getAuthor方法，让其的返回值就是封装好的Optional的话，我们在使用时就会方便很多
         * ，而且在实际开发中我们的数据很多是从数据库获取的，Mybatis从3.5版本可以也已经支持Optional了
         * ，我们可以直接把dao方法的返回值类型定义成Optional类型，MyBatis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。
         */
        Optional<Author> optionalAuthor = getOptionalAuthor();
        System.out.println(optionalAuthor);

        /*
         * Tip-3
         * 如果我们确定一个对象不是空的则可以使用Optional的静态方法 of() 来把数据封装成Optional对象。
         */
        Optional.of(author);
        //Optional.of(null); //不能传入null ! 会直接抛出空指针异常


        /*
         * Tip-6 安全获取值，不推荐使用默认的 get() 方法！
         * orElseGet(): 获取数据并且设置数据为空时的默认值，如果数据不为空就能获取到该数据，如果为空则根据我们传入的参数来创建对象作为默认值返回。
         * orElseThrow(): 获取数据，如果数据不为空就能获取到该数据，如果为空则根据你传入的参数来创建异常抛出。
         */
        Optional<Author> optionalAuthor1 = getOptionalAuthor();
        Author author2 = optionalAuthor1.orElseGet(() -> new Author());
        System.out.println(author2.getName());
        try {
            Author author3 = optionalAuthor1.orElseThrow(() -> new RuntimeException("数据为null"));
            System.out.println(author3.getName());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        /*
         * Tip-7 过滤
         * 我们可以使用 filter() 方法对数据进行过滤，如果原本是有数据的，但是不符合判断，也会变成一个无数据的Optional对象。
         */
        Optional<Author> optionalAuthor2 = getOptionalAuthor();
        optionalAuthor2.filter(a2 -> a2.getAge() > 18).ifPresent(a4 -> System.out.println(a4.getName()));

        /*
         * Tip-8 判断
         * 我们可以使用 isPresent() 方法进行是否存在数据的判断，如果为空返回值为false, 如果不为空，返回值为true
         * , 但是这种方式并不能体现Optional的好处，更推荐使用 ifPresent() 方法。
         */
        if (optionalAuthor2.isPresent()) {
            System.out.println(optionalAuthor2.get().getName());
        }

        /*
         * Tip-9 数据转换
         * optional还提供了 map() 可以让我们的对数据进行转换，并且转换得到的数据也还是被Optional包装好的
         * ，保证了我们的使用安全，例如我们想获取作家的书籍集合：
         */
        optionalAuthor2.map(a3 -> a3.getBooks())
                .ifPresent(books -> System.out.println(books));
    }

    public static Optional<Author> getOptionalAuthor() {
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        //return Optional.ofNullable(author);

        /*
         * Tip-4
         * 如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null
         * ，这个时候就需要把null封装成optional对象返回，这时则可以使用Optional的静态方法 empty() 来进行封装。
         */
        return author == null ? Optional.empty() : Optional.of(author);
    }

    public static Author getAuthor() {
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        return author;
    }
}
